/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.rum.gui.component;

import cz.muni.fi.rum.domainmodel.ReflectorListener;
import cz.muni.fi.rum.domainmodel.ReflectorSession;
import cz.muni.fi.rum.sender.Factory;
import cz.muni.fi.rum.sender.SenderException;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

/**
 *
 * @author pmikulasek
 */
public class TreeTransferHandler extends TransferHandler {

    DataFlavor listenerNodeFlavor;
    DataFlavor[] flavors = new DataFlavor[1];
    ListenerNode[] listenerNodesToRemove;
    SessionNode[] sessionNodesToRemove;

    public TreeTransferHandler() {
        try {
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType
                    + ";class=\""
                    + ListenerNode[].class.getName()
                    + "\"";
            listenerNodeFlavor = new DataFlavor(mimeType);
            flavors[0] = listenerNodeFlavor;
        } catch (ClassNotFoundException e) {
            System.out.println("ClassNotFound: " + e.getMessage());
        }
    }

    public boolean canImport(TransferHandler.TransferSupport support) {
        //je tohle drop operace?
        if (!support.isDrop()) {
            return false;
        }
        //pokud je to drop operace, tak ukazuj drop lokaci(jinak by mohlo vyhodit vyjimku)
        support.setShowDropLocation(true);
        //podporuje tato drop operace listenerNodeFlavor DataFlavor?
        //obsahuje ListenerNode.class?
        if (!support.isDataFlavorSupported(listenerNodeFlavor)) {
            return false;
        }
        // Do not allow a drop on the drag source selections.
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
        JTree tree = (JTree) support.getComponent();
        int dropRow = tree.getRowForPath(dl.getPath());
        int[] selRows = tree.getSelectionRows();
        for (int i = 0; i < selRows.length; i++) {
            if (selRows[i] == dropRow) {
                return false;
            }
        }

        // Do not allow a non-leaf node to be copied to a level
        // which is less than its source level.
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode target = (DefaultMutableTreeNode) dest.getLastPathComponent();
        if (target instanceof ListenerNode || target instanceof FolderNode) {
            return false;
        }
        if (target instanceof FolderNode) {
        }

        TreePath path = tree.getPathForRow(selRows[0]);
        DefaultMutableTreeNode firstNode = (DefaultMutableTreeNode) path.getLastPathComponent();
        if (firstNode.getChildCount() > 0
                && target.getLevel() < firstNode.getLevel()) {
            return false;
        }
        return true;
    }

    //pravdepodobne metoda pro vytvoreni transportniho baliku pro prenos
    protected Transferable createTransferable(JComponent c) {
        JTree tree = (JTree) c;
        TreePath[] paths = tree.getSelectionPaths();
        if (paths != null) {
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<ListenerNode> copies = new ArrayList<ListenerNode>();
            List<ListenerNode> listenerToRemove = new ArrayList<ListenerNode>();
            List<SessionNode> sessionToRemove = new ArrayList<SessionNode>();
            for (TreePath path : paths) {
                Object lastPathComponent = path.getLastPathComponent();
                if (lastPathComponent instanceof ListenerNode) {
                    ListenerNode node = (ListenerNode) lastPathComponent;
                    if (!listenerToRemove.contains(node)) {
                        listenerToRemove.add(node);
                        copies.add(copy(node));
                        TreeNode parent = node.getParent();
                        if (parent.getChildCount() == 1) {
                            if (!sessionToRemove.contains((SessionNode) parent)) {
                                sessionToRemove.add((SessionNode) parent);
                            }
                        }
                    }
                } else if (lastPathComponent instanceof SessionNode) {
                    SessionNode node = (SessionNode) lastPathComponent;
                    if (!sessionToRemove.contains(node)) {
                        sessionToRemove.add(node);
                    }
                    for (int i = 0; i < node.getChildCount(); i++) {
                        TreeNode child = node.getChildAt(i);
                        if (child instanceof ListenerNode) {
                            ListenerNode childNode = (ListenerNode) child;
                            if (!listenerToRemove.contains(childNode)) {
//                                listenerToRemove.add(childNode);
                                copies.add(copy(childNode));
                            }
                        }
                    }
                }
            }

            ListenerNode[] nodes = copies.toArray(new ListenerNode[copies.size()]);
            listenerNodesToRemove = listenerToRemove.toArray(new ListenerNode[listenerToRemove.size()]);
            sessionNodesToRemove = sessionToRemove.toArray(new SessionNode[sessionToRemove.size()]);
            return new ListenerNodesTransferable(nodes);
        }
        return null;
    }

    /** Defensive copy used in createTransferable. */
    private ListenerNode copy(ListenerNode node) {
        return (ListenerNode) node.clone();
    }

    //pravdepodobne metoda pro dokonceni operace MOVE zavola se po uvolneni mysi...
    //TODO v tomto miste nejspis vytvoreni requestu pro odebrani ze session
    //a taky odebrani ze session v modelu
    protected void exportDone(JComponent source, Transferable data, int action) {
        if ((action & MOVE) == MOVE) {
            JTree tree = (JTree) source;
            TreeModel model = (TreeModel) tree.getModel();
            model.removeSessionNodes(sessionNodesToRemove);
            model.removeListenerNodes(listenerNodesToRemove);
        }
    }

    public int getSourceActions(JComponent c) {
        return COPY_OR_MOVE;
    }

    //metoda pro dokonceni transferu... vlozi data na misto kam se muzou vlozit. po uvoloneni mysi.
    public boolean importData(TransferHandler.TransferSupport support) {
        if (!canImport(support)) {
            return false;
        }
        // Extract transfer data.
        ListenerNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (ListenerNode[]) t.getTransferData(listenerNodeFlavor);
        } catch (UnsupportedFlavorException ufe) {
            //TODO nejspis jen logovani ve stavu info
            System.out.println("UnsupportedFlavor: " + ufe.getMessage());
        } catch (java.io.IOException ioe) {
            //TODO nejspis jen logovani ve stavu info
            System.out.println("I/O error: " + ioe.getMessage());
        }
        // Get drop location info.
        JTree.DropLocation dl = (JTree.DropLocation) support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        //TODO nejspis pretypovani
        SessionNode parent = (SessionNode) dest.getLastPathComponent();
        JTree tree = (JTree) support.getComponent();
        TreeModel model = (TreeModel) tree.getModel();
        // Configure for drop mode.
        int index = childIndex;    // DropMode.INSERT
        if (childIndex == -1) {     // DropMode.ON
            index = parent.getChildCount();
        }
        model.addListenerNodes(nodes, parent, index);

        createRemoveRequests();
        createConnectSessionsRequest((ReflectorSession) parent.getContent());
        return true;
    }

    private void createConnectSessionsRequest(ReflectorSession session) {
        if (session.size() != 1) {
            Map<String, String> params = session.getAllParams();
            if (!params.isEmpty()) {
                try {
                    Factory.getSenderManager().addRequest("Method.connect.session", params);
                } catch (SenderException ex) {
                    //TODO dodelat hlaseni o nenalezeni teto rapzpravy
                    Logger.getLogger(TreeModel.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    private void createRemoveRequests() {
        if (listenerNodesToRemove.length > 0) {
            for (ListenerNode node : listenerNodesToRemove) {
                ReflectorListener listener = (ReflectorListener) node.getContent();
                String name = listener.getName();
                if (name != null) {
                    Map<String, String> param = new HashMap<String, String>(1);
                    param.put(ReflectorListener.LISTENER_NAME_KEY, name);
                    try {
                        Factory.getSenderManager().addRequest("Method.remove.listener.form.session", param);
                    } catch (SenderException ex) {
                    //TODO dodelat hlaseni o nenalezeni teto rapzpravy
                        Logger.getLogger(TreeTransferHandler.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }
    }

    public String toString() {
        return getClass().getName();
    }

    public class ListenerNodesTransferable implements Transferable {

        ListenerNode[] nodes;

        public ListenerNodesTransferable(ListenerNode[] nodes) {
            this.nodes = nodes;
        }

        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
            if (!isDataFlavorSupported(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return nodes;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return listenerNodeFlavor.equals(flavor);
        }
    }
}
